module AXI_Lite_Arbiter(
  input ACLK ,
  input ARESETn,
  
  //AXI-Lite Slave interface 0
  input [63:0] AWADDR_0,
  input [2:0] AWPROT_0,
  input AWVALID_0,
  input [63:0] WDATA_0,
  input [7:0] WSTRB_0,
  input WVALID_0,
  input BREADY_0,
  input [63:0] ARADDR_0,
  input [2:0] ARPROT_0,
  input ARVALID_0,
  input RREADY_0,
  output reg AWREADY_0,
  output reg WREADY_0,
  output reg [1:0] BRESP_0,
  output reg BVALID_0,
  output reg ARREADY_0,
  output reg [63:0] RDATA_0,
  output reg [1:0] RRESP_0,
  output reg RVALID_0,

  //AXI-Lite Slave interface 1
  input [63:0] AWADDR_1,
  input [2:0] AWPROT_1,
  input AWVALID_1,
  input [63:0] WDATA_1,
  input [7:0] WSTRB_1,
  input WVALID_1,
  input BREADY_1,
  input [63:0] ARADDR_1,
  input [2:0] ARPROT_1,
  input ARVALID_1,
  input RREADY_1,
  output reg AWREADY_1,
  output reg WREADY_1,
  output reg [1:0] BRESP_1,
  output reg BVALID_1,
  output reg ARREADY_1,
  output reg [63:0] RDATA_1,
  output reg [1:0] RRESP_1,
  output reg RVALID_1,

  //AXI-Lite Master interface
  output reg [63:0] AWADDR,
  output reg [2:0] AWPROT,
  output reg AWVALID,
  output reg [63:0] WDATA,
  output reg [7:0] WSTRB,
  output reg WVALID,
  output reg BREADY,
  output reg [63:0] ARADDR,
  output reg [2:0] ARPROT,
  output reg ARVALID,
  output reg RREADY,
  input AWREADY,
  input WREADY,
  input [1:0] BRESP,
  input BVALID,
  input ARREADY,
  input [63:0] RDATA,
  input [1:0] RRESP,
  input RVALID

);
  wire clk = ACLK;
  wire rst = ~ARESETn;
  
  localparam FSM_IDLE = 3'b001;
  localparam FSM_SEL0 = 3'b010;
  localparam FSM_SEL1 = 3'b100;
  reg [2:0] cstate,nstate;
  
  wire trans_end = (RVALID&RREADY) | (BVALID&BREADY);
  wire trans_request_0 = (AWVALID_0|ARVALID_0);
  wire trans_request_1 = (AWVALID_1|ARVALID_1);

  reg idle;
  always @(posedge clk) begin
    if(rst) begin
      cstate <= FSM_IDLE;
    end else begin
      cstate <= nstate;
    end
  end

  always @(*) begin
    nstate = cstate;
    case(cstate)
    FSM_IDLE: if(trans_request_0) nstate = FSM_SEL0; else if(trans_request_1) nstate = FSM_SEL1;
    FSM_SEL0: if((idle|trans_end) & (trans_request_1&~trans_request_0)) nstate = FSM_SEL1;
    FSM_SEL1: if((idle|trans_end) & (trans_request_0&~trans_request_1)) nstate = FSM_SEL0;
    default: nstate = FSM_IDLE;
    endcase
  end

  always @(posedge clk) begin
    if(rst) begin
      idle <= 1'b0;
    end else begin
      case(cstate)
      FSM_IDLE: idle <= 1'b0;
      FSM_SEL0: if(trans_request_0) idle <= 1'b0; else if(trans_end) idle <= 1'b1;
      FSM_SEL1: if(trans_request_1) idle <= 1'b0; else if(trans_end) idle <= 1'b1;
      default: idle <= 1'b0;
      endcase
    end
  end

  localparam BUS_WIDTH_O = 64+3+1+64+8+1+1+64+3+1+1;
  localparam BUS_WIDTH_I = 1+1+2+1+1+64+2+1;
  wire [BUS_WIDTH_O-1:0] slave_0,slave_1;
  wire [BUS_WIDTH_I-1:0] master;
  assign slave_0 = {AWADDR_0,AWPROT_0,AWVALID_0,WDATA_0,WSTRB_0,WVALID_0,BREADY_0,ARADDR_0,ARPROT_0,ARVALID_0,RREADY_0};
  assign slave_1 = {AWADDR_1,AWPROT_1,AWVALID_1,WDATA_1,WSTRB_1,WVALID_1,BREADY_1,ARADDR_1,ARPROT_1,ARVALID_1,RREADY_1};
  assign master = {AWREADY,WREADY,BRESP,BVALID,ARREADY,RDATA,RRESP,RVALID};
  always @(*) begin
    case(cstate)
    FSM_IDLE: begin
      {AWREADY_0,WREADY_0,BRESP_0,BVALID_0,ARREADY_0,RDATA_0,RRESP_0,RVALID_0} = {BUS_WIDTH_I{1'b0}};
      {AWREADY_1,WREADY_1,BRESP_1,BVALID_1,ARREADY_1,RDATA_1,RRESP_1,RVALID_1} = {BUS_WIDTH_I{1'b0}};
      {AWADDR,AWPROT,AWVALID,WDATA,WSTRB,WVALID,BREADY,ARADDR,ARPROT,ARVALID,RREADY} = {BUS_WIDTH_O{1'b0}};
    end
    FSM_SEL0: begin
      {AWREADY_0,WREADY_0,BRESP_0,BVALID_0,ARREADY_0,RDATA_0,RRESP_0,RVALID_0} = master;
      {AWREADY_1,WREADY_1,BRESP_1,BVALID_1,ARREADY_1,RDATA_1,RRESP_1,RVALID_1} = {BUS_WIDTH_I{1'b0}};
      {AWADDR,AWPROT,AWVALID,WDATA,WSTRB,WVALID,BREADY,ARADDR,ARPROT,ARVALID,RREADY} = slave_0;
    end
    FSM_SEL1: begin
      {AWREADY_0,WREADY_0,BRESP_0,BVALID_0,ARREADY_0,RDATA_0,RRESP_0,RVALID_0} = {BUS_WIDTH_I{1'b0}};
      {AWREADY_1,WREADY_1,BRESP_1,BVALID_1,ARREADY_1,RDATA_1,RRESP_1,RVALID_1} = master;
      {AWADDR,AWPROT,AWVALID,WDATA,WSTRB,WVALID,BREADY,ARADDR,ARPROT,ARVALID,RREADY} = slave_1;
    end
    default:  begin
      {AWREADY_0,WREADY_0,BRESP_0,BVALID_0,ARREADY_0,RDATA_0,RRESP_0,RVALID_0} = {BUS_WIDTH_I{1'b0}};
      {AWREADY_1,WREADY_1,BRESP_1,BVALID_1,ARREADY_1,RDATA_1,RRESP_1,RVALID_1} = {BUS_WIDTH_I{1'b0}};
      {AWADDR,AWPROT,AWVALID,WDATA,WSTRB,WVALID,BREADY,ARADDR,ARPROT,ARVALID,RREADY} = {BUS_WIDTH_O{1'b0}};
    end
    endcase
  end
endmodule
